#include "../inc/ff_utils.h"

namespace feifei
{
	static std::vector<TaskPool*> taskPools;

#ifdef __unix__
#if TIMER_THREAD
	static pthread_t hTimerThread = 0;
	static bool timerRunning = false;
	void TimerCallback(int signo)
	{
		for (TaskPool * pool : taskPools)
			pool->TickIsr();
	}
	static void * timerThread(void * arg)
	{
		struct sigaction act;

		act.sa_handler = TimerCallback;
		act.sa_flags = 0;
		sigemptyset(&act.sa_mask);
		sigaction(SIGPROF, &act, NULL);

		struct itimerval val;

		val.it_value.tv_sec = 0;
		val.it_value.tv_usec = 1000 * 1000 / TASK_POOL_TICKS_PER_SEC;
		val.it_interval = val.it_value;
		setitimer(ITIMER_PROF, &val, NULL);

		timerRunning = true;
		while (timerRunning) { sleep(5); }
		return 0;
	}
	void TaskPool::init()
	{
		if (hTimerThread == 0)
		{
			pthread_create(&hTimerThread, NULL, timerThread, NULL);
		}
	}
	void TaskPool::release()
	{
		timerRunning = false;
		pthread_join(hTimerThread, NULL);
	}
#else
	void TaskPool::release() {}
	void TaskPool::init()
	{
		struct sigaction act;

		act.sa_handler = TimerCallback;
		act.sa_flags = 0;
		sigemptyset(&act.sa_mask);
		sigaction(SIGPROF, &act, NULL);

		struct itimerval val;

		val.it_value.tv_sec = 0;
		val.it_value.tv_usec = 1000;
		val.it_interval = val.it_value;
		setitimer(ITIMER_PROF, &val, NULL);
	}
#endif
#endif

#ifdef _WIN32
#if TIMER_THREAD
	static HANDLE hTimer = nullptr;
	static HANDLE hTimerThread = nullptr;
	static bool timerRunning = false;
	void __stdcall TimerCallback(void *lpParam, DWORD, DWORD)
	{
		for (TaskPool * pool : taskPools)
			pool->TickIsr();
	}
	static DWORD WINAPI timerThread(LPVOID lpParam)
	{
		hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

		LARGE_INTEGER liDueTime;
		liDueTime.QuadPart = 0;

		PTIMERAPCROUTINE callbackFunc = TimerCallback;
		SetWaitableTimer(hTimer, &liDueTime, 1000 / TASK_POOL_TICKS_PER_SEC,
			callbackFunc, NULL, FALSE);

		timerRunning = true;
		while (timerRunning) { SleepEx(30000, TRUE); }
		return 0;
	}
	void TaskPool::init()
	{
		DWORD threadId;
		if (hTimerThread == nullptr)
		{
			hTimerThread = CreateThread(NULL, 0, timerThread, this, 0, &threadId);
			CloseHandle(hTimerThread);
		}
	}
	void TaskPool::release()
	{
		if (hTimer != NULL)
		{
			CancelWaitableTimer(hTimer);
			CloseHandle(hTimer);
			hTimer = nullptr;
		}
		hTimerThread = nullptr;
		timerRunning = false;
	}
#else
	void WINAPI TimerCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2)
	{
		for (TaskPool * pool : taskPools)
			pool->TickIsr();
	}
	void TaskPool::release() {}
	void TaskPool::init()
	{
		DWORD_PTR dwUser = NULL;
		int timerID = timeSetEvent(1000 / TASK_POOL_TICKS_PER_SEC, 1, (LPTIMECALLBACK)TimerCallback, dwUser, TIME_PERIODIC);
	}
#endif
#endif

	TaskPool::TaskPool()
	{
		taskPools.push_back(this);

		runTicCnt = 0;
		slicesTicCnt = 0;
		busyTicCnt = 0;
	}
	TaskPool::~TaskPool()
	{
		for (Task* t : lowTasks)
			delete t;
		for (Task* t : normalTasks)
			delete t;
		for (Task* t : highTasks)
			delete t;
		
		if (taskPools.size() == 1)
		{
			release();
			LOG("Release task pool timer.");
		}
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////
using namespace feifei;
TaskPool * testPool;
Task * tsk_test1;
Task * tsk_test2;
Task * tsk_test3;
Signal * sig_test2;
Signal * sig_test3;
void TSK_TestTask1(void)
{
	LOG("hello task 1.");
	LOG("Task pool usage = %%%.2f.", testPool->Usage() * 100);
	feifei::ffSleepMilliSec(400);
//	LOG("post signal 2.");	sig_test2->Post();
	LOG("post signal 3.");	sig_test3->Post();
	feifei::ffSleepMilliSec(300);
	LOG("byebye task 1.");
}
void TSK_TestTask2(void)
{
	if (sig_test2->Pend(100) == false)
	{
		LOG("task 2 not pend.");
	}
	else
	{
		LOG("task 2 pend signal 2.");
	}
}
void TSK_TestTask3(void)
{
	LOG("task 3 pend signal 3.");
}
void RunTaskPoolTest()
{
	testPool = new TaskPool();
	tsk_test1 = testPool->AddTask(TSK_TestTask1, E_TaskPriori::NORMAL, 2000);
	tsk_test2 = testPool->AddTask(TSK_TestTask2, E_TaskPriori::NORMAL, 500);
	tsk_test3 = testPool->AddTask(TSK_TestTask3, E_TaskPriori::NORMAL);
	sig_test2 = testPool->AddSignal(tsk_test2);
	sig_test3 = testPool->AddSignal(tsk_test3);
	testPool->Start();
}
